package naga.x.game.template;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import naga.x.common.Reloadable;
import naga.x.service.Service;
import naga.x.service.ServiceException;

/**
 * 模板管理
 * @author yang.li
 *
 */
public class TemplateService implements Service, Reloadable {
	
	protected Map<Class<? extends Template>, TIntObjectMap<? extends Template>> templates = new HashMap<Class<? extends Template>, TIntObjectMap<? extends Template>>();
	protected Map<Class<? extends Template>, List<? extends Template>> templatesList = new HashMap<Class<? extends Template>, List<? extends Template>>();
	
	protected static Set<Class<?>> registeredTemplateClasses = new LinkedHashSet<Class<?>>();
	protected static final Logger LOG = LoggerFactory.getLogger(TemplateService.class);
	
	public static void register(Class<?> templateClass) {
		registeredTemplateClasses.add(templateClass);
	}
	
	@Override
	public void startup() throws ServiceException {
		TemplateParser parser = new TemplateParser(this);
		for (Class<?> clazz : registeredTemplateClasses) {
			try {
				parser.parse(clazz);
			} catch (TemplateException ex) {
				LOG.error("[TEMPLATE PARSE ERROR]" + clazz, ex);
				System.exit(1);
			}
		}
		compact();
		// do gc
		registeredTemplateClasses = null;
		parser = null;
	}
	
	private void compact() {
		Map<Class<? extends Template>, List<? extends Template>> dup = new HashMap<Class<? extends Template>, List<? extends Template>>(templatesList.size());
		Iterator<Entry<Class<? extends Template>, List<? extends Template>>> itr = templatesList.entrySet().iterator();
		while (itr.hasNext()) {
			Entry<Class<? extends Template>, List<? extends Template>> e = itr.next();
			List<? extends Template> val = new ArrayList<>(e.getValue());
			dup.put(e.getKey(), val);
		}
		templatesList = dup;
	}
	
	@Override
	public void shutdown() throws ServiceException {
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public void reload() {
		templates.clear();
		templatesList.clear();
		try {
			startup();
		} catch (ServiceException e) {
			throw new RuntimeException(e);
		}
	}
	
	public <T extends Template> T get(Class<T> templateClass, int id) {
		TIntObjectMap<T> map = (TIntObjectMap<T>) templates.get(templateClass);
		if (map != null) {
			return map.get(id);
		}
		return null;
	}
	
	public <T extends Template> TIntObjectMap<T> getTemplates(Class<T> templateClass) {
		return (TIntObjectMap<T>) templates.get(templateClass);
	}
	
	public <T extends Template> List<T> getTemplatesList(Class<T> templateClass) {
//		TIntObjectMap<T> map = (TIntObjectMap<T>) templates.get(templateClass);
//		if (map != null) {
//			return Arrays.asList(map.values((T[]) Array.newInstance(templateClass, map.size())));
//		}
		List<T> list = (List<T>) templatesList.get(templateClass);
		return list != null ? list : Collections.emptyList();
	}
	
	public <T extends Template> void add(Class<T> key, T template) {
		if (template.hasId()) {
			TIntObjectMap<T> map = (TIntObjectMap<T>) templates.get(key);
			if (map == null) {
				map = new TIntObjectHashMap<T>();
				templates.put(key, map);
			}
			map.put(template.getId(), template);
		}
		List<T> list = (List<T>) templatesList.get(key);
		if (list == null) {
			list = new ArrayList<T>();
			templatesList.put(key, list);
		}
		list.add(template);
	}
	
	public <T extends Template> void add(T template) {
		add((Class<T>) template.getClass(), template);
	}
	
	public <T extends Template> void remove(T template) {
		remove(template.getClass(), template.getId());
	}
	
	public <T extends Template> void remove(Class<T> templateClass, int id) {
		if (id == Template.HAS_NO_ID) {
			return;
		}
		TIntObjectMap<T> map = (TIntObjectMap<T>) templates.get(templateClass);
		if (map != null) {
			T t = map.remove(id);
			List<T> list = (List<T>) templatesList.get(templateClass);
			if (list != null) {
				list.remove(t);
			}
		}
	}
	
	public <T extends Template> void remove(Class<T> templateClass) {
		templates.remove(templateClass);
		templatesList.remove(templateClass);
	}

}
